home *** CD-ROM | disk | FTP | other *** search
/ OpenGL Superbible (2nd Edition) / OpenGL SuperBible e2.iso / tools / GLUT-3.7 / PROGS / CONTRIB / AGVIEWER.C < prev    next >
Encoding:
C/C++ Source or Header  |  1998-08-12  |  12.9 KB  |  498 lines

  1. /*
  2.  * agviewer.c  (version 1.0)
  3.  *
  4.  * AGV: a glut viewer. Routines for viewing a 3d scene w/ glut
  5.  *
  6.  * See agv_example.c and agviewer.h comments within for more info.
  7.  *
  8.  * I welcome any feedback or improved versions!
  9.  *
  10.  * Philip Winston - 4/11/95
  11.  * pwinston@hmc.edu
  12.  * http://www.cs.hmc.edu/people/pwinston
  13.  */
  14.  
  15. #include <GL/glut.h>
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <math.h>
  19.  
  20. #include "agviewer.h"
  21.  
  22. /* Some <math.h> files do not define M_PI... */
  23. #ifndef M_PI
  24. #define M_PI 3.14159265358979323846
  25. #endif
  26.  
  27. /***************************************************************/
  28. /************************** SETTINGS ***************************/
  29. /***************************************************************/
  30.  
  31.    /* Initial polar movement settings */
  32. #define INIT_POLAR_AZ  0.0
  33. #define INIT_POLAR_EL 30.0
  34. #define INIT_DIST      8.0
  35. #define INIT_AZ_SPIN   0.5
  36. #define INIT_EL_SPIN   0.0
  37.  
  38.   /* Initial flying movement settings */
  39. #define INIT_EX        0.0
  40. #define INIT_EY       -2.0
  41. #define INIT_EZ       -2.0
  42. #define INIT_MOVE     0.01
  43. #define MINMOVE      0.001    
  44.  
  45.   /* Start in this mode */
  46. #define INIT_MODE   POLAR   
  47.  
  48.   /* Controls:  */
  49.  
  50.   /* map 0-9 to an EyeMove value when number key is hit in FLYING mode */
  51. #define SPEEDFUNCTION(x) ((x)*(x)*0.001)  
  52.  
  53.   /* Multiply EyeMove by (1+-MOVEFRACTION) when +/- hit in FLYING mode */
  54. #define MOVEFRACTION 0.25   
  55.  
  56.   /* What to multiply number of pixels mouse moved by to get rotation amount */
  57. #define EL_SENS   0.5
  58. #define AZ_SENS   0.5
  59.  
  60.   /* What to multiply number of pixels mouse moved by for movement amounts */
  61. #define DIST_SENS 0.01
  62. #define E_SENS    0.01
  63.  
  64.   /* Minimum spin to allow in polar (lower forced to zero) */
  65. #define MIN_AZSPIN 0.1
  66. #define MIN_ELSPIN 0.1
  67.  
  68.   /* Factors used in computing dAz and dEl (which determine AzSpin, ElSpin) */
  69. #define SLOW_DAZ 0.90
  70. #define SLOW_DEL 0.90
  71. #define PREV_DAZ 0.80
  72. #define PREV_DEL 0.80
  73. #define CUR_DAZ  0.20
  74. #define CUR_DEL  0.20
  75.  
  76. /***************************************************************/
  77. /************************** GLOBALS ****************************/
  78. /***************************************************************/
  79.  
  80. int     MoveMode = INIT_MODE;  /* FLYING or POLAR mode? */
  81.  
  82. GLfloat Ex = INIT_EX,             /* flying parameters */
  83.         Ey = INIT_EY,
  84.         Ez = INIT_EZ,
  85.         EyeMove = INIT_MOVE,     
  86.  
  87.         EyeDist = INIT_DIST,      /* polar params */
  88.         AzSpin  = INIT_AZ_SPIN,
  89.         ElSpin  = INIT_EL_SPIN,
  90.  
  91.         EyeAz = INIT_POLAR_AZ,    /* used by both */
  92.         EyeEl = INIT_POLAR_EL;
  93.  
  94. int agvMoving;    /* Currently moving?  */
  95.  
  96. int downx, downy,   /* for tracking mouse position */
  97.     lastx, lasty,
  98.     downb = -1;     /* and button status */
  99.                         
  100. GLfloat downDist, downEl, downAz, /* for saving state of things */
  101.         downEx, downEy, downEz,   /* when button is pressed */
  102.         downEyeMove;                
  103.  
  104. GLfloat dAz, dEl, lastAz, lastEl;  /* to calculate spinning w/ polar motion */
  105. int     AdjustingAzEl = 0;
  106.  
  107. int AllowIdle, RedisplayWindow; 
  108.    /* If AllowIdle is 1 it means AGV will install its own idle which
  109.     * will update the viewpoint as needed and send glutPostRedisplay() to the
  110.     * window RedisplayWindow which was set in agvInit().  AllowIdle of 0
  111.     * means AGV won't install an idle funciton, and something like
  112.     * "if (agvMoving) agvMove()" should exist at the end of the running
  113.     * idle function.
  114.     */
  115.  
  116. #define MAX(x,y) (((x) > (y)) ? (x) : (y))
  117. #define TORAD(x) ((M_PI/180.0)*(x))
  118. #define TODEG(x) ((180.0/M_PI)*(x))
  119.  
  120. /***************************************************************/
  121. /************************ PROTOTYPES ***************************/
  122. /***************************************************************/
  123.  
  124.   /*
  125.    * these are functions meant for internal use only
  126.    * the other prototypes are in agviewer.h
  127.    */
  128.  
  129. void PolarLookFrom(GLfloat dist, GLfloat elevation, GLfloat azimuth);
  130. void FlyLookFrom(GLfloat x, GLfloat y, GLfloat z, GLfloat az, GLfloat el);
  131. int  ConstrainEl(void);
  132. void MoveOn(int v);
  133. void SetMove(float newmove);
  134. static void normalize(GLfloat v[3]);
  135. static void ncrossprod(float v1[3], float v2[3], float cp[3]);
  136.  
  137.  
  138. /***************************************************************/
  139. /************************ agvInit ******************************/
  140. /***************************************************************/
  141.  
  142. void agvInit(int window)
  143. {
  144.   glutMouseFunc(agvHandleButton);
  145.   glutMotionFunc(agvHandleMotion);
  146.   glutKeyboardFunc(agvHandleKeys);
  147.   RedisplayWindow = glutGetWindow();
  148.   agvSetAllowIdle(window);
  149. }
  150.  
  151. /***************************************************************/
  152. /************************ VIEWPOINT STUFF **********************/
  153. /***************************************************************/
  154.  
  155.   /*
  156.    * viewing transformation modified from page 90 of red book
  157.    */
  158. void PolarLookFrom(GLfloat dist, GLfloat elevation, GLfloat azimuth)
  159. {
  160.   glTranslatef(0, 0, -dist);
  161.   glRotatef(elevation, 1, 0, 0);
  162.   glRotatef(azimuth, 0, 1, 0);
  163.  
  164. }
  165.  
  166.   /*
  167.    * I took the idea of tracking eye position in absolute
  168.    * coords and direction looking in Polar form from denis
  169.    */
  170. void FlyLookFrom(GLfloat x, GLfloat y, GLfloat z, GLfloat az, GLfloat el)
  171. {
  172.   float lookat[3], perp[3], up[3];
  173.  
  174.   lookat[0] = sin(TORAD(az))*cos(TORAD(el));
  175.   lookat[1] = sin(TORAD(el));
  176.   lookat[2] = -cos(TORAD(az))*cos(TORAD(el));
  177.   normalize(lookat);
  178.   perp[0] = lookat[2];
  179.   perp[1] = 0;
  180.   perp[2] = -lookat[0];
  181.   normalize(perp);
  182.   ncrossprod(lookat, perp, up);
  183.   gluLookAt(x, y, z,
  184.             x+lookat[0], y+lookat[1], z+lookat[2],
  185.             up[0], up[1], up[2]);
  186. }
  187.  
  188.   /*
  189.    * Call viewing transformation based on movement mode
  190.    */
  191. void agvViewTransform(void)
  192.   switch (MoveMode) {
  193.     case FLYING:
  194.       FlyLookFrom(Ex, Ey, Ez, EyeAz, EyeEl);
  195.       break;
  196.     case POLAR:
  197.       PolarLookFrom(EyeDist, EyeEl, EyeAz);
  198.       break;
  199.     }
  200. }
  201.  
  202.   /*
  203.    * keep them vertical; I think this makes a lot of things easier, 
  204.    * but maybe it wouldn't be too hard to adapt things to let you go
  205.    * upside down
  206.    */
  207. int ConstrainEl(void)
  208. {
  209.   if (EyeEl <= -90) {
  210.     EyeEl = -89.99;
  211.     return 1;
  212.   } else if (EyeEl >= 90) {
  213.     EyeEl = 89.99;
  214.     return 1;
  215.   }
  216.   return 0;
  217. }
  218.  
  219.  /*
  220.   * Idle Function - moves eyeposition
  221.   */
  222. void agvMove(void)
  223. {
  224.  
  225.   switch (MoveMode)  {
  226.     case FLYING:
  227.       Ex += EyeMove*sin(TORAD(EyeAz))*cos(TORAD(EyeEl));
  228.       Ey += EyeMove*sin(TORAD(EyeEl));
  229.       Ez -= EyeMove*cos(TORAD(EyeAz))*cos(TORAD(EyeEl));
  230.       break;
  231.  
  232.     case POLAR:
  233.       EyeEl += ElSpin;
  234.       EyeAz += AzSpin;
  235.       if (ConstrainEl()) {  /* weird spin thing to make things look     */
  236.         ElSpin = -ElSpin;      /* look better when you are kept from going */
  237.                                /* upside down while spinning - Isn't great */
  238.         if (fabs(ElSpin) > fabs(AzSpin))
  239.           AzSpin = fabs(ElSpin) * ((AzSpin > 0) ? 1 : -1);
  240.       }
  241.       break;
  242.     }
  243.  
  244.   if (AdjustingAzEl) {
  245.     dAz *= SLOW_DAZ;
  246.     dEl *= SLOW_DEL;
  247.   }
  248.  
  249.   if (AllowIdle) {
  250.     glutSetWindow(RedisplayWindow);
  251.     glutPostRedisplay();
  252.   }
  253. }
  254.  
  255.  
  256.   /*
  257.    * Don't install agvMove as idle unless we will be updating the view
  258.    * and we've been given a RedisplayWindow
  259.    */
  260. void MoveOn(int v)
  261. {
  262.   if (v && ((MoveMode == FLYING && EyeMove != 0) ||
  263.              (MoveMode == POLAR &&
  264.              (AzSpin != 0 || ElSpin != 0 || AdjustingAzEl)))) {
  265.     agvMoving = 1;
  266.     if (AllowIdle)
  267.       glutIdleFunc(agvMove);
  268.   } else {
  269.     agvMoving = 0;
  270.     if (AllowIdle)
  271.       glutIdleFunc(NULL);
  272.   }
  273. }
  274.  
  275.   /*
  276.    * set new redisplay window.  If <= 0 it means we are not to install
  277.    * an idle function and will rely on whoever does install one to 
  278.    * put statement like "if (agvMoving) agvMove();" at end of it
  279.    */
  280. void agvSetAllowIdle(int allowidle)
  281. {
  282.   if ((AllowIdle = allowidle))
  283.     MoveOn(1);
  284. }
  285.  
  286.  
  287.   /*
  288.    * when moving to flying we stay in the same spot, moving to polar we
  289.    * reset since we have to be looking at the origin (though a pivot from
  290.    * current position to look at origin might be cooler)
  291.    */
  292. void agvSwitchMoveMode(int move)
  293. {
  294.   switch (move) {
  295.     case FLYING:
  296.       if (MoveMode == FLYING) return;
  297.       Ex    = -EyeDist*sin(TORAD(EyeAz))*cos(TORAD(EyeEl));
  298.       Ey    =  EyeDist*sin(TORAD(EyeEl));
  299.       Ez    =  EyeDist*(cos(TORAD(EyeAz))*cos(TORAD(EyeEl)));
  300.       EyeAz =  EyeAz;
  301.       EyeEl = -EyeEl;
  302.       EyeMove = INIT_MOVE;
  303.       break;
  304.     case POLAR:
  305.       EyeDist = INIT_DIST;
  306.       EyeAz   = INIT_POLAR_AZ;
  307.       EyeEl   = INIT_POLAR_EL;
  308.       AzSpin  = INIT_AZ_SPIN;
  309.       ElSpin  = INIT_EL_SPIN;
  310.       break;
  311.     }
  312.   MoveMode = move;
  313.   MoveOn(1);
  314.   glutPostRedisplay();
  315. }
  316.  
  317. /***************************************************************/
  318. /*******************    MOUSE HANDLING   ***********************/
  319. /***************************************************************/
  320.  
  321. void agvHandleButton(int button, int state, int x, int y)
  322. {
  323.  if (state == GLUT_DOWN && downb == -1) {  
  324.     lastx = downx = x;
  325.     lasty = downy = y;
  326.     downb = button;    
  327.  
  328.     switch (button) {
  329.       case GLUT_LEFT_BUTTON:
  330.         lastEl = downEl = EyeEl;
  331.         lastAz = downAz = EyeAz;
  332.         AzSpin = ElSpin = dAz = dEl = 0;
  333.         AdjustingAzEl = 1;
  334.     MoveOn(1);
  335.         break;
  336.  
  337.       case GLUT_MIDDLE_BUTTON:
  338.         downDist = EyeDist;
  339.     downEx = Ex;
  340.     downEy = Ey;
  341.     downEz = Ez;
  342.     downEyeMove = EyeMove;
  343.     EyeMove = 0;
  344.     }
  345.  
  346.   } else if (state == GLUT_UP && button == downb) {
  347.  
  348.     downb = -1;
  349.  
  350.     switch (button) {
  351.       case GLUT_LEFT_BUTTON:
  352.         if (MoveMode != FLYING) {
  353.       AzSpin =  -dAz;
  354.       if (AzSpin < MIN_AZSPIN && AzSpin > -MIN_AZSPIN)
  355.         AzSpin = 0;    
  356.       ElSpin = -dEl;
  357.       if (ElSpin < MIN_ELSPIN && ElSpin > -MIN_ELSPIN)
  358.         ElSpin = 0; 
  359.     }
  360.         AdjustingAzEl = 0;
  361.         MoveOn(1);
  362.     break;
  363.  
  364.       case GLUT_MIDDLE_BUTTON:
  365.     EyeMove = downEyeMove;
  366.       }
  367.   }
  368. }
  369.  
  370.  /*
  371.   * change EyeEl and EyeAz and position when mouse is moved w/ button down
  372.   */
  373. void agvHandleMotion(int x, int y)
  374. {
  375.   int deltax = x - downx, deltay = y - downy;
  376.  
  377.   switch (downb) {
  378.     case GLUT_LEFT_BUTTON:
  379.       EyeEl  = downEl + EL_SENS * ((MoveMode == FLYING) ? -deltay : deltay);
  380.       ConstrainEl();
  381.       EyeAz  = downAz + AZ_SENS * deltax;
  382.       dAz    = PREV_DAZ*dAz + CUR_DAZ*(lastAz - EyeAz);
  383.       dEl    = PREV_DEL*dEl + CUR_DEL*(lastEl - EyeEl);
  384.       lastAz = EyeAz;
  385.       lastEl = EyeEl;
  386.       break;
  387.     case GLUT_MIDDLE_BUTTON:
  388.         EyeDist = downDist + DIST_SENS*deltay;
  389.         Ex = downEx - E_SENS*deltay*sin(TORAD(EyeAz))*cos(TORAD(EyeEl));
  390.         Ey = downEy - E_SENS*deltay*sin(TORAD(EyeEl));
  391.         Ez = downEz + E_SENS*deltay*cos(TORAD(EyeAz))*cos(TORAD(EyeEl));
  392.       break;
  393.   }
  394.   glutPostRedisplay();
  395. }
  396.  
  397. /***************************************************************/
  398. /********************* KEYBOARD HANDLING ***********************/
  399. /***************************************************************/
  400.  
  401.   /*
  402.    * set EyeMove (current speed) for FLYING mode
  403.    */
  404. void SetMove(float newmove)
  405. {
  406.   if (newmove > MINMOVE) {
  407.     EyeMove = newmove;
  408.     MoveOn(1);
  409.   } else {
  410.     EyeMove = 0;
  411.     MoveOn(0);
  412.   }
  413. }
  414.  
  415.   /*
  416.    * 0->9 set speed, +/- adjust current speed  -- in FLYING mode
  417.    */
  418. /* ARGSUSED1 */
  419. void agvHandleKeys(unsigned char key, int x, int y)
  420. {
  421.   if (MoveMode != FLYING)
  422.     return;
  423.  
  424.   if (key >= '0' && key <= '9')
  425.     SetMove(SPEEDFUNCTION((key-'0')));
  426.   else
  427.     switch(key) {
  428.       case '+':  
  429.         if (EyeMove == 0)
  430.           SetMove(MINMOVE);
  431.          else
  432.       SetMove(EyeMove *= (1 + MOVEFRACTION));
  433.         break;
  434.       case '-':
  435.     SetMove(EyeMove *= (1 - MOVEFRACTION));
  436.         break;
  437.     }
  438. }
  439.  
  440. /***************************************************************/
  441. /*********************** VECTOR STUFF **************************/
  442. /***************************************************************/
  443.  
  444.   /* normalizes v */
  445. static void normalize(GLfloat v[3])
  446. {
  447.   GLfloat d = sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
  448.  
  449.   if (d == 0)
  450.     fprintf(stderr, "Zero length vector in normalize\n");
  451.   else
  452.     v[0] /= d; v[1] /= d; v[2] /= d;
  453. }
  454.  
  455.   /* calculates a normalized crossproduct to v1, v2 */
  456. static void ncrossprod(float v1[3], float v2[3], float cp[3])
  457. {
  458.   cp[0] = v1[1]*v2[2] - v1[2]*v2[1];
  459.   cp[1] = v1[2]*v2[0] - v1[0]*v2[2];
  460.   cp[2] = v1[0]*v2[1] - v1[1]*v2[0];
  461.   normalize(cp);
  462. }
  463.  
  464. /***************************************************************/
  465. /**************************** AXES *****************************/
  466. /***************************************************************/
  467.  
  468.  
  469.   /* draw axes -- was helpful to debug/design things */
  470. void agvMakeAxesList(int displaylistnum)
  471. {
  472.   int i,j;
  473.   GLfloat axes_ambuse[] =   { 0.5, 0.0, 0.0, 1.0 };
  474.   glNewList(displaylistnum, GL_COMPILE);
  475.   glPushAttrib(GL_LIGHTING_BIT);
  476.   glMatrixMode(GL_MODELVIEW);
  477.     glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, axes_ambuse);
  478.     glBegin(GL_LINES);
  479.       glVertex3f(15, 0, 0); glVertex3f(-15, 0, 0);
  480.       glVertex3f(0, 15, 0); glVertex3f(0, -15, 0);
  481.       glVertex3f(0, 0, 15); glVertex3f(0, 0, -15);
  482.     glEnd();
  483.     for (i = 0; i < 3; i++) {
  484.       glPushMatrix();
  485.         glTranslatef(-10*(i==0), -10*(i==1), -10*(i==2));
  486.         for (j = 0; j < 21; j++) {
  487.           glutSolidCube(0.1);
  488.           glTranslatef(i==0, i==1, i==2);
  489.     }
  490.       glPopMatrix();
  491.     }
  492.   glPopAttrib();
  493.   glEndList();  
  494. }
  495.  
  496.  
  497.